<?php

class cache_memcached
{
    public $conf = array();
    /**
     * @var Memcached $link
     */
    public $link = NULL;
    public $cachepre = '';
    public $errno = 0;
    public $errstr = '';
    public $ismemcache = FALSE;

    public function __construct($conf = array())
    {
        if (!extension_loaded('Memcache') && !extension_loaded('Memcached')) {
            return $this->error(1, ' Memcached 扩展没有加载，请检查您的 PHP 版本');
        }
        $this->conf = $conf;
        $this->cachepre = isset($conf['cachepre']) ? $conf['cachepre'] : 'pre_';
        return true;
    }

    public function connect()
    {
        $conf = $this->conf;
        if ($this->link) return $this->link;
        if (extension_loaded('Memcache')) {
            $this->ismemcache = TRUE;
            $memcache = new Memcache;
            $r = $memcache->connect($conf['host'], $conf['port']);
        } elseif (extension_loaded('Memcached')) {
            $this->ismemcache = FALSE;
            $memcache = new Memcached;
            $r = $memcache->addserver($conf['host'], $conf['port']);
        } else {
            return $this->error(-1, 'Memcache 扩展不存在。');
        }
        if (!$r) {
            return $this->error(-1, '连接 Memcached 服务器失败。');
        }
        $this->link = $memcache;
        return $this->link;
    }

    public function set($k, $v, $life = 0)
    {
        if (!$this->link && !$this->connect()) return FALSE;
        if ($this->ismemcache) {
            $r = $this->link->set($k, $v, 0, $life);
        } else {
            $r = $this->link->set($k, $v, $life);
        }
        return $r;
    }

    public function get($k)
    {
        if (!$this->link && !$this->connect()) return FALSE;
        $r = $this->link->get($k);
        return $r === FALSE ? NULL : $r;
    }

    public function delete($k)
    {
        if (!$this->link && !$this->connect()) return FALSE;
        return $this->link->delete($k);
    }

    public function truncate()
    {
        if (!$this->link && !$this->connect()) return FALSE;
        return $this->link->flush();
    }

    public function error($errno = 0, $errstr = '')
    {
        $this->errno = $errno;
        $this->errstr = $errstr;
        DEBUG and trigger_error('Cache Error:' . $this->errstr);
        return true;
    }

    public function __destruct()
    {
    }
}

class cache_redis
{
    public $conf = array();
    /**
     * @var Redis $link
     */
    public $link = NULL;
    public $cachepre = '';
    public $errno = 0;
    public $errstr = '';

    public function __construct($conf = array())
    {
        if (!extension_loaded('Redis')) {
            $this->error(-1, ' Redis 扩展没有加载');
            return;
        }
        $this->conf = $conf;
        $this->cachepre = isset($conf['cachepre']) ? $conf['cachepre'] : 'pre_';
    }

    public function connect()
    {
        if ($this->link) return $this->link;
        $redis = new Redis;
        $r = $redis->connect($this->conf['host'], $this->conf['port']);
        if (!$r) {
            $this->error(-1, '连接 Redis 服务器失败。');
            return false;
        }
        //如果设置了密码，则验证密码
        if ($this->conf['password']) {
            if (!($redis->auth($this->conf['password']))) {
                $this->error(-1, '验证失败');
                return false;
            }
        }

        $this->link = $redis;
        $this->link->select($this->conf['index']);
        return true;
    }

    public function set($k, $v, $life = 0)
    {
        if (!$this->link && !$this->connect()) return FALSE;
        $v = json_encode($v);
        if ($life) {
            $r = $this->link->set($k, $v, $life);
        } else {
            $r = $this->link->set($k, $v);
        }
        return $r;
    }

    public function get($k)
    {
        if (!$this->link && !$this->connect()) return FALSE;
        $r = $this->link->get($k);
        return $r === FALSE ? NULL : safe_json_decode($r);
    }

    public function delete($k)
    {
        if (!$this->link && !$this->connect()) return FALSE;
        return $this->link->del($k) ? TRUE : FALSE;
    }

    public function truncate()
    {
        if (!$this->link && !$this->connect()) return FALSE;
        return $this->link->flushdb();
    }

    public function error($errno = 0, $errstr = '')
    {
        $this->errno = $errno;
        $this->errstr = $errstr;
        DEBUG and trigger_error('Cache Error:' . $this->errstr);
    }

    public function __destruct()
    {
    }
}

/**
 * 创建新的缓存实例，默认自动调用
 * @param $cacheconf
 * @return bool|cache_memcached|cache_redis|null
 */
function cache_new($cacheconf)
{
    if ($cacheconf && !empty($cacheconf['enable'])) {
        switch ($cacheconf['type']) {
            case 'redis':
                $cache = new cache_redis($cacheconf['redis']);
                break;
            case 'memcached':
                $cache = new cache_memcached($cacheconf['memcached']);
                break;
            default:
                echo '不支持的 cache type:' . $cacheconf['type'];
                return null;
        }
        return $cache;
    }
    return null;
}

/**
 * 获取缓存
 * 【需开启配置中的缓存】，已自动初始化，直接可以使用
 * @param string $k 键
 * @param Redis $c
 * @return bool | string 值
 */
function cache_get($k, $c = NULL)
{
    $cache = getGlobals('cache');
    $c = $c ? $c : $cache;
    if (!$c) return FALSE;
    strlen($k) > 32 and $k = md5($k);
    $k = $c->cachepre . $k;
    $v = $c->get($k);
    return $v === false ? null : $v;
}

/** 设置缓存
 * 【需开启配置中的缓存】，已自动初始化，直接可以使用
 * @param $k
 * @param $v
 * @param int $life
 * @param null $c
 * @return bool
 */
function cache_set($k, $v, $life = 0, $c = NULL)
{
    $cache = getGlobals('cache');
    $c = $c ? $c : $cache;
    if (!$c) return FALSE;
    strlen($k) > 32 and $k = md5($k);
    $k = $c->cachepre . $k;
    return $c->set($k, $v, $life);
}

/** 删除缓存
 * 【需开启配置中的缓存】，已自动初始化，直接可以使用
 * @param $k
 * @param null $c
 * @return bool
 */
function cache_delete($k, $c = NULL)
{
    $cache = getGlobals('cache');
    $c = $c ? $c : $cache;
    if (!$c) return FALSE;
    strlen($k) > 32 and $k = md5($k);
    $k = $c->cachepre . $k;
    return $c->delete($k);
}

/** 清空缓存
 * 【需开启配置中的缓存】，已自动初始化，直接可以使用
 * @param null $c
 * @return bool
 */
function cache_truncate($c = NULL)
{
    $cache = getGlobals('cache');
    $c = $c ? $c : $cache;
    if (!$c) return FALSE;
    return $c->truncate();
}

/**
 * 是否启用了缓存存储
 * @return bool
 */
function cache_is_opend()
{
    return getGlobals('cache') ? true : false;
}

/**
 * 添加键名到缓存列表，用于清空缓存使用
 * 存储到redis或memcache中时使用
 * @param $keyname
 */
function _cache_site_list_addkey($keyname)
{
    $sitecachelist = cache_get("sitecachelist");
    if ($sitecachelist) {
        $sitecachelist = unserialize($sitecachelist);
    }

    if (!is_array($sitecachelist)) {
        $sitecachelist = array();
    }
    $sitecachelist[$keyname] = time();

    cache_set('sitecachelist', serialize($sitecachelist));
}

/**
 * 移除键名，从缓存列表中
 * 存储到redis或memcache中时使用
 * @param $keyname
 * @return bool
 */
function _cache_site_list_removekey($keyname)
{
    $sitecachelist = cache_get("sitecachelist");
    if ($sitecachelist) {
        $sitecachelist = unserialize($sitecachelist);
    }

    if (!is_array($sitecachelist)) {
        return true;
    }
    unset($sitecachelist[$keyname]);
    return cache_set('sitecachelist', serialize($sitecachelist));

}

/**
 * 从缓存列表获取缓存存入时间
 * 存储到redis或memcache中时使用
 * @param $keyname
 * @return bool|mixed
 */
function _cache_site_list_getval($keyname)
{
    $sitecachelist = cache_get("sitecachelist");
    if ($sitecachelist) {
        $sitecachelist = unserialize($sitecachelist);
    }

//    dump_pre($sitecachelist);
    if (!is_array($sitecachelist)) {
        $sitecachelist = array();
    }
    return isset($sitecachelist[$keyname]) ? $sitecachelist[$keyname] : false;
}

/**
 * 返回缓存到缓存系统的关联数组
 * 存储到redis或memcache中时使用
 * @return array
 */
function _cache_site_list_getlist()
{
    $sitecachelist = cache_get("sitecachelist");
    if ($sitecachelist) {
        $sitecachelist = unserialize($sitecachelist);
    }

    if (!is_array($sitecachelist)) {
        $sitecachelist = array();
    }
    return $sitecachelist;
}

/**
 * 获取缓存，如果启用了redis或memcached，则从缓存应用系统中获取，否则从文件中获取
 * @param string $keyname 键
 * @param int $overtime 过期时间
 * @param string $keykind 分类
 * @return bool|false|mixed 内容
 */
function cache_site_get($keyname, $overtime = 604800, $keykind = '')
{
    if (cache_is_opend()) {

        $key = 'site_' . md5($keykind . $keyname);
        $value = cache_get($key);
        if ($value === null) {
            $value = false;
            _cache_site_list_removekey($key);
        } else {
            $value = unserialize($value);
        }

        return $value;
    }

    $filename = cache_site_path($keyname, $keykind);
    if (is_file($filename) && filemtime($filename) + $overtime > time()) {
        return unserialize(file_get_contents($filename));
    } else {
        return false;
    }
}

/**
 * 生成缓存，如果启用了redis或memcached，则存储到缓存应用系统中，否则设置到文件中
 * @param string $keyname 键
 * @param mixed $value 值
 * @param int $overtime 缓存时间，秒
 * @param string $keykind 分类
 * @return bool 是否成功
 */
function cache_site_set($keyname, $value, $overtime = 604800, $keykind = '')
{
    if (cache_is_opend()) {
        $key = 'site_' . md5($keykind . $keyname);
        _cache_site_list_addkey($key);
        $value = serialize($value);
//不能大于30天，否则memcache无法读取值，这里设定最大30天的秒数-10秒
        if ($overtime > 2591990) $overtime = 2591990;

        return cache_set($key, $value, $overtime);
    }

    $filename = cache_site_path($keyname, $keykind);

    if (!is_dir(dirname($filename))) {
        file_mkdirs(dirname($filename), 0777);
    }
    file_put_contents($filename, serialize($value));
    return true;
}

/**
 * 删除缓存，如果启用了redis或memcached，则操作缓存应用系统，否则操作文件缓存
 * @param string $keyname 键
 * @param string $keykind 分类
 * @return bool 是否成功
 */
function cache_site_del($keyname, $keykind = '')
{
    if (cache_is_opend()) {
        $key = 'site_' . md5($keykind . $keyname);
        _cache_site_list_removekey($key);
        return cache_delete($key);
    }

    $filename = cache_site_path($keyname, $keykind);
    if (file_exists($filename)) {
        @unlink($filename);
    }
    return true;
}

/**
 * 缓存文件的最后修改时间，如果启用了redis或memcached，则操作缓存应用系统，否则操作文件缓存
 * @param string $keyname 键
 * @param string $keykind 分类
 * @return bool|false|int 最后修改的时间
 */
function cache_site_lasttime($keyname, $keykind = '')
{
    if (cache_is_opend()) {
        $key = 'site_' . md5($keykind . $keyname);
        return _cache_site_list_getval($key);
    }

    $filename = cache_site_path($keyname, $keykind);
    return filemtime($filename);
}


/**
 * 生成md5的缓存文件路径，缓存到文件系统时使用
 * @param string $keyname 键
 * @param string $keykind 分类
 * @return string 获取文件路径
 */
function cache_site_path($keyname, $keykind = '')
{
    $md5 = md5(server_name() . server_port() . $keyname . SiteHash . UrlRewrite);
    if (!empty($keykind)) {
        $keykind .= DIRECTORY_SEPARATOR;
    }
    return CacheDir . $keykind . substr($md5, 0, 4) . DIRECTORY_SEPARATOR . substr($md5, 4, 4) . DIRECTORY_SEPARATOR . substr($md5, 8, 20) . '.html';
}